import React, { useState, createElement, useCallback } from 'react'
import ReactFlow, { addEdge, MiniMap, Controls, Background, useNodesState, useEdgesState, updateEdge } from 'reactflow'
import { nodes as initialNodes, edges as initialEdges } from './initial-elements'
import { message } from 'antd'
import dagre from 'dagre'
import styles from './index.less'
import ReactDOM from 'react-dom/client'
import ResizableNodeSelected from './ResizableNodeSelected'

import 'reactflow/dist/style.css'

const dagreGraph = new dagre.graphlib.Graph()
dagreGraph.setDefaultEdgeLabel(() => ({}))

type MindNode = {
  id: string
  type: 'node' | 'label'
  label: string
  position: {
    x: number
    y: number
  }
}

type MindConnection = {
  id: string
  type: 'connection'
  from: string
  to: string
}

type MindMapProps = {
  nodes: MindNode[]
  connections: MindConnection[]
}

const minimapStyle = {
  height: 120,
}

const nodeTypes = {
  ResizableNodeSelected,
}

const nodeWidth = 172
const nodeHeight = 36

//   布局调整
const getLayoutedElements = (nodes: any[], edges: any[], direction = 'TB') => {
  const isHorizontal = direction === 'LR'
  dagreGraph.setGraph({ rankdir: direction })

  nodes.forEach((node) => {
    dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight })
  })

  edges.forEach((edge) => {
    dagreGraph.setEdge(edge.source, edge.target)
  })

  dagre.layout(dagreGraph)

  nodes.forEach((node) => {
    const nodeWithPosition = dagreGraph.node(node.id)
    node.targetPosition = isHorizontal ? 'left' : 'top'
    node.sourcePosition = isHorizontal ? 'right' : 'bottom'

    // We are shifting the dagre node position (anchor=center center) to the top left
    // so it matches the React Flow node anchor point (top left).
    node.position = {
      x: nodeWithPosition.x - nodeWidth / 2,
      y: nodeWithPosition.y - nodeHeight / 2,
    }

    return node
  })

  return { nodes, edges }
}

const MindMap: React.FC<MindMapProps> = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes as any)
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
  const onConnect = (params: any) => setEdges((eds) => addEdge(params, eds))
  // 线位置更新
  const onEdgeUpdate = useCallback(
    (oldEdge: any, newConnection: any) => setEdges((els) => updateEdge(oldEdge, newConnection, els)),
    []
  )

  // 布局
  const onLayout = useCallback(
    (direction: string | undefined) => {
      const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(nodes, edges, direction)

      setNodes([...layoutedNodes])
      setEdges([...layoutedEdges])
    },
    [nodes, edges]
  )

  // 增加模块
  const [name, setName] = useState('')
  const addNode = () => {
    if (!name) {
      message.error('请输入模块名称')
      return
    }
    setNodes((e) =>
      e.concat({
        id: (e.length + 1).toString(),
        data: { label: `${name}` },
        position: {
          x: 0,
          y: 0,
        },
        // position: {
        //   x: Math.random() * window.innerWidth,
        //   y: Math.random() * window.innerHeight,
        // },
      })
    )
    setName('')
    message.success('添加成功')
  }
  // 保存节点数据到本地存储
  const saveNode = () => {
    // const newNodes = nodes.map((node: any) => {
    //   return {
    //     ...node,
    //     data: { ...node.data, label: node.data.label + '' },
    //   }
    // })
    // console.log(newNodes)
    const data = { nodes, edges }
    localStorage.setItem('mindMapData', JSON.stringify(data))
  }
  //   node 节点转换成真实html结构
  const nodeToHtml = (label: any) => {
    if (typeof label !== 'string') {
      return <>{ReactDOM.createRoot(document.createElement('div')).render(label)}</>
    }
    return label
  }
  // 从本地存储加载节点数据
  const loadNode = () => {
    const data = localStorage.getItem('mindMapData')
    if (data) {
      const { nodes, edges } = JSON.parse(data)
      const newNodes = nodes.map((node: any) => {
        return {
          ...node,
          data: { ...node.data, label: nodeToHtml(node.data.label) },
        }
      })
      console.log(newNodes)
      setNodes(newNodes)
      setEdges(edges)
    }
  }
  // 刷新页面
  const refreshPage = () => {
    window.location.reload()
  }

  return (
    <div style={{ height: '750px', width: '100%' }}>
      <ReactFlow
        nodes={nodes} // 节点
        edges={edges} // 连接线
        onNodesChange={onNodesChange} // 节点拖拽等改变
        onEdgesChange={onEdgesChange} // 连接线拖拽等改变
        onConnect={onConnect} // 节点直接连接
        fitView // 渲染节点数据
        attributionPosition="top-right" // react-flow的位置,类似水印,可以通过css隐藏
        nodeTypes={nodeTypes} // 自定义节点
        onEdgeUpdate={onEdgeUpdate} // 连接线更新
      >
        <MiniMap
          style={minimapStyle}
          zoomable
          pannable
        />
        <Controls />
        <Background
          color="#aaa"
          gap={16}
        />
      </ReactFlow>
      <div className={styles.addNode}>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          name="title"
          value={name}
        />
        <button
          type="button"
          onClick={addNode}
        >
          添加新节点
        </button>
        <button
          type="button"
          onClick={saveNode}
        >
          保存画布
        </button>
        <button
          type="button"
          onClick={loadNode}
        >
          加载画布
        </button>
        <button
          type="button"
          onClick={refreshPage}
        >
          刷新画布
        </button>

        <button
          type="button"
          onClick={() => onLayout('TB')}
        >
          垂直树布局
        </button>
        <button
          type="button"
          onClick={() => onLayout('LR')}
        >
          平整树布局
        </button>
      </div>
    </div>
  )
}

export default MindMap
